
import videoShader from "./video3dShader"
var ratateDirection = {
    'LEFT': 'Z',
    'RIGHT': '-Z',
    'TOP': 'Y',
    'BOTTOM': '-Y',
    'ALONG': 'X',
    'INVERSE': '-X'
};


//视频融合（投射3D，贴物体表面）
//原理：在可视域的基础上，着色器里传入纹理，再计算UV进行贴图
function video3D(viewer, Cesium, options) {
    extensionProperty(this);
    var textStyles = {
        font: '50px 楷体',
        fill: true,
        fillColor: new Cesium.Color(1.0, 1.0, 0.0, 1.0),
        stroke: true,
        strokeWidth: 2,
        strokeColor: new Cesium.Color(1.0, 1.0, 1.0, 0.8),
        backgroundColor: new Cesium.Color(1.0, 1.0, 1.0, 0.1),
        textBaseline: 'top',
        padding: 40
    };
    if (!options) options = {};
    this.viewer = viewer;
    this.Cesium = Cesium;
    this._cameraPosition = options.cameraPosition; //相机位置
    this._position = options.position; //视点位置
    this.type = options.type; //投影类型
    this.alpha = options.alpha || 1.0; //透明度
    this.color = options.color; //投影的颜色
    this._debugFrustum = Cesium.defaultValue(options.debugFrustum, true); //显示视椎体
    this._aspectRatio = options.aspectRatio || this._getWinWidHei(); //宽高比
    var fov = options.fov && Cesium.Math.toRadians(options.fov); //相机水平张角
    this._camerafov = fov || this.viewer.scene.camera.frustum.fov; //相机水平张角
    this.videoTexture = this.texture = options.texture || new Cesium.Texture({ //默认材质
        context: this.viewer.scene.context,
        source: {
            width: 1,
            height: 1,
            arrayBufferView: new Uint8Array([255, 255, 255, 255])
        },
        flipY: false
    });
    this._videoPlay = Cesium.defaultValue(options.videoPlay, true); //暂停播放
    this.defaultShow = Cesium.defaultValue(options.show, true); //显示和隐藏
    this.clearBlack = Cesium.defaultValue(options.clearBlack, false); //消除鱼眼视频的黑色
    this._rotateDeg = 1;
    this._dirObj = Cesium.defaultValue(options.dirObj, undefined);
    this.text = Cesium.defaultValue(options.text, undefined);
    this.textStyles = Cesium.defaultValue(options.textStyles, textStyles);
    this._disViewColor = Cesium.defaultValue(options.disViewColor, new Cesium.Color(0, 0, 0, 0.5));

    if (!this._cameraPosition || !this._position) {
        console.log("初始化失败：请确认相机位置与视点位置正确！");
        return;
    }
    //传入了DOM
    if (options.dom) {
        this.dom = options.dom;
        if (this.dom instanceof HTMLElement) {
            this.dom = options.dom;
        }
        if (options.dom instanceof Object && options.dom.length) {
            this.dom = options.dom[0];
        }
    }
    //传入了URL
    this.url = options.url; //url

    switch (this.type) {
        default:
            case "Video":
            this.activeVideo(this.url);
        break;
        case "Image":
                this.activePicture(this.url);
            this.deActiveVideo();
            break;
        case "Color":
                this.activeColor(this.color);
            this.deActiveVideo();
            break;
        case "Text":
                this.activeText(this.text, this.textStyles);
            this.deActiveVideo();
            break;
    }
    this._createShadowMap();
    this._getOrientation();
    this._addCameraFrustum();
    this._addPostProcess();
    this.viewer.scene.primitives.add(this);

}

//旋转相机
video3D.prototype.rotateCamera = function(axis, deg) {
        let Cesium = this.Cesium;
        var rotateDegree = Cesium.defaultValue(deg, this._rotateDeg);
        switch (axis) {
            case ratateDirection.LEFT:
                break;
            case ratateDirection.RIGHT:
                rotateDegree *= -1;
                break;
            case ratateDirection.TOP:
                break;
            case ratateDirection.BOTTOM:
                rotateDegree *= -1;
                break;
            case ratateDirection.ALONG:
                break;
            case ratateDirection.INVERSE:
                rotateDegree *= -1;
                break;
        }
        var newDir = this._computedNewViewDir(axis, rotateDegree);

        this.viewer.scene.postProcessStages.remove(this.postProcess);
        this.viewer.scene.primitives.remove(this.cameraFrustum);
        this.viewShadowMap.destroy();
        this.cameraFrustum.destroy();
        this._resetCameraDir(newDir);
        this._getOrientation();
        this._addCameraFrustum();
        this._addPostProcess();
    }
    //计算新视点
video3D.prototype._resetCameraDir = function(dirObj) {
        if (!dirObj || !dirObj.up || !dirObj.right || !dirObj.direction) return;
        this._dirObj = dirObj;
        this._createShadowMap();
    }
    //计算新视点
video3D.prototype._computedNewViewDir = function(axis, deg) {
    let Cesium = this.Cesium;
    deg = Cesium.Math.toRadians(deg);
    var camera = this.viewShadowMap._lightCamera;
    var oldDir = Cesium.clone(camera.direction);
    var oldRight = Cesium.clone(camera.right);
    var oldTop = Cesium.clone(camera.up);
    var mat3 = new Cesium.Matrix3();

    switch (axis) {
        case ratateDirection.LEFT:
            Cesium.Matrix3.fromRotationZ(deg, mat3);
            break;
        case ratateDirection.RIGHT:
            Cesium.Matrix3.fromRotationZ(deg, mat3);
            break;
        case ratateDirection.TOP:
            Cesium.Matrix3.fromRotationY(deg, mat3);
            break;
        case ratateDirection.BOTTOM:
            Cesium.Matrix3.fromRotationY(deg, mat3);
            break;
        case ratateDirection.ALONG:
            Cesium.Matrix3.fromRotationX(deg, mat3);
            break;
        case ratateDirection.INVERSE:
            Cesium.Matrix3.fromRotationX(deg, mat3);
            break;
    }
    var localToWorld_Matrix = Cesium.Transforms.eastNorthUpToFixedFrame(camera.position);
    var worldToLocal_Matrix = Cesium.Matrix4.inverse(localToWorld_Matrix, new Cesium.Matrix4());

    var localDir = Cesium.Matrix4.multiplyByPointAsVector(worldToLocal_Matrix, oldDir, new Cesium.Cartesian3());
    var localNewDir = Cesium.Matrix3.multiplyByVector(mat3, localDir, new Cesium.Cartesian3());
    var newDir = Cesium.Matrix4.multiplyByPointAsVector(localToWorld_Matrix, localNewDir, new Cesium.Cartesian3());

    var localRight = Cesium.Matrix4.multiplyByPointAsVector(worldToLocal_Matrix, oldRight, new Cesium.Cartesian3());
    var localNewRight = Cesium.Matrix3.multiplyByVector(mat3, localRight, new Cesium.Cartesian3());
    var newRight = Cesium.Matrix4.multiplyByPointAsVector(localToWorld_Matrix, localNewRight, new Cesium.Cartesian3());

    var localTop = Cesium.Matrix4.multiplyByPointAsVector(worldToLocal_Matrix, oldTop, new Cesium.Cartesian3());
    var localNewTop = Cesium.Matrix3.multiplyByVector(mat3, localTop, new Cesium.Cartesian3());
    var newTop = Cesium.Matrix4.multiplyByPointAsVector(localToWorld_Matrix, localNewTop, new Cesium.Cartesian3());
    return {
        direction: newDir,
        right: newRight,
        up: newTop
    };
}


video3D.prototype.getPercentagePoint = function(cartesian) {
        let Cesium = this.Cesium;
        if (!cartesian) return;
        var vm = this.viewShadowMap._lightCamera._viewMatrix;
        var pm = this.viewShadowMap._lightCamera.frustum.projectionMatrix;
        var c4 = new Cesium.Cartesian4(cartesian.x, cartesian.y, cartesian.z, 1.0);
        var pvm = Cesium.Matrix4.multiply(pm, vm, new Cesium.Matrix4());
        var epos1 = Cesium.Matrix4.multiplyByVector(pvm, c4, new Cesium.Cartesian4());
        var epos2 = new Cesium.Cartesian2(epos1.x / epos1.w, epos1.y / epos1.w);
        var epos3 = new Cesium.Cartesian2(epos2.x / 2 + 0.5, epos2.y / 2 + 0.5);
        return epos3;
    }
    //改变相机的水平张角
video3D.prototype._changeCameraFov = function() {
    this.viewer.scene.postProcessStages.remove(this.postProcess);
    this.viewer.scene.primitives.remove(this.cameraFrustum);
    this._createShadowMap();
    this._getOrientation();
    this._addCameraFrustum();
    this._addPostProcess();
}

//改变相机视野的宽高比例（垂直张角）
video3D.prototype._changeVideoWidHei = function() {
    this.viewer.scene.postProcessStages.remove(this.postProcess);
    this.viewer.scene.primitives.remove(this.cameraFrustum);
    this._createShadowMap();
    this._getOrientation();
    this._addCameraFrustum();
    this._addPostProcess();
}

//改变相机的位置
video3D.prototype._changeCameraPos = function() {
    this.viewer.scene.postProcessStages.remove(this.postProcess);
    this.viewer.scene.primitives.remove(this.cameraFrustum);
    this.viewShadowMap.destroy();
    this.cameraFrustum.destroy();
    this._createShadowMap(true);
    this._getOrientation();
    this._addCameraFrustum();
    this._addPostProcess();
}

//改变相机视点的位置
video3D.prototype._changeViewPos = function() {
    this.viewer.scene.postProcessStages.remove(this.postProcess);
    this.viewer.scene.primitives.remove(this.cameraFrustum);
    this.viewShadowMap.destroy();
    this.cameraFrustum.destroy();
    this._createShadowMap(true);
    this._getOrientation();
    this._addCameraFrustum();
    this._addPostProcess();
}

video3D.prototype._switchShow = function() {
    if (this.show) {
        !this.postProcess && this._addPostProcess();
    } else {
        this.viewer.scene.postProcessStages.remove(this.postProcess);
        delete this.postProcess;
        this.postProcess = null;
    }
}

//激活或重置视频URL
video3D.prototype.activeVideo = function(videoSrc) {
    //在可视域添加视频
    var videoElement;
    if (this.dom) {
        videoElement = this.dom;
    } else {
        videoElement = this._createVideoEle(videoSrc);
    }

    var that = this;
    if (videoElement) {
        this.type = "Video";
        this.videoElement = videoElement;
        videoElement.addEventListener("canplaythrough", function() {
            that.viewer.clock.onTick.addEventListener(that.activeVideoListener, that);
        });
    }
}

video3D.prototype.activeVideoListener = function() {
    let Cesium = this.Cesium;
    try {
        if (this._videoPlay && this.videoElement.paused) this.videoElement.play();
    } catch (e) {}

    this.videoTexture && this.videoTexture.destroy();
    this.videoTexture = new Cesium.Texture({
        context: this.viewer.scene.context,
        source: this.videoElement,
        pixelFormat: Cesium.PixelFormat.RGBA,
        pixelDatatype: Cesium.PixelDatatype.UNSIGNED_BYTE
    });
}

//删除视频播放监听
video3D.prototype.deActiveVideo = function() {
    this.viewer.clock.onTick.removeEventListener(this.activeVideoListener, this);
    delete this.activeVideoListener;
}

//激活或重置图片URL
video3D.prototype.activePicture = function(picSrc) {
    //在可视域添加图片    
    this.videoTexture = this.texture;

    var that = this;
    var image = new Image();
    image.onload = function() {
        that.type = "Image";
        that.videoTexture = new Cesium.Texture({
            context: that.viewer.scene.context,
            source: image
        });
    };
    image.onerror = function() {
        console.log('图片加载失败：' + picSrc);
    };
    image.src = picSrc;
}

//激活或重置颜色
video3D.prototype.activeColor = function(color) {
    let Cesium = this.Cesium;
    //在可视域添加纯色
    var that = this;
    this.type = "Color";
    var r, g, b, a;
    if (color) {
        r = color.red * 255;
        g = color.green * 255;
        b = color.blue * 255;
        a = color.alpha * 255;
    } else {
        r = Math.random() * 255;
        g = Math.random() * 255;
        b = Math.random() * 255;
        a = Math.random() * 255;
    }
    that.videoTexture = new Cesium.Texture({
        context: that.viewer.scene.context,
        source: {
            width: 1,
            height: 1,
            arrayBufferView: new Uint8Array([r, g, b, a])
        },
        flipY: false
    });
}

//激活或重置文本
video3D.prototype.activeText = function(text, styles) {
    let Cesium = this.Cesium;
    var that = this;
    this.type = "Text";
    if (!text) return;
    styles = styles || {};
    styles.textBaseline = 'top';
    this.textCanvas = Cesium.writeTextToCanvas(text, styles);
    that.videoTexture = new Cesium.Texture({
        context: that.viewer.scene.context,
        source: this.textCanvas,
        flipY: true
    });
}

//呈现投影相机的第一视角
video3D.prototype.locate = function() {
    let Cesium = this.Cesium;
    var camera_pos = Cesium.clone(this.cameraPosition);
    var lookat_pos = Cesium.clone(this.position);
    this.viewer.camera.position = camera_pos;
    if (this._dirObj) {
        this.viewer.camera.direction = Cesium.clone(this._dirObj.direction);
        this.viewer.camera.right = Cesium.clone(this._dirObj.right);
        this.viewer.camera.up = Cesium.clone(this._dirObj.up);
        return;
    }
    this.viewer.camera.direction = Cesium.Cartesian3.subtract(lookat_pos, camera_pos, new Cesium.Cartesian3(0, 0, 0));
    this.viewer.camera.up = Cesium.Cartesian3.normalize(camera_pos, new Cesium.Cartesian3(0, 0, 0));
}

//获取四元数
video3D.prototype._getOrientation = function() {
    let Cesium = this.Cesium;
    var cpos = this.cameraPosition;
    var position = this.position;
    var direction = Cesium.Cartesian3.normalize(Cesium.Cartesian3.subtract(position, cpos, new Cesium.Cartesian3()), new Cesium.Cartesian3());
    var up = Cesium.Cartesian3.normalize(cpos, new Cesium.Cartesian3());
    var camera = new Cesium.Camera(this.viewer.scene);
    camera.position = cpos;
    camera.direction = direction;
    camera.up = up;
    direction = camera.directionWC;
    up = camera.upWC;
    var right = camera.rightWC;
    var scratchRight = new Cesium.Cartesian3();
    var scratchRotation = new Cesium.Matrix3();
    var scratchOrientation = new Cesium.Quaternion();

    right = Cesium.Cartesian3.negate(right, scratchRight);
    var rotation = scratchRotation;
    Cesium.Matrix3.setColumn(rotation, 0, right, rotation);
    Cesium.Matrix3.setColumn(rotation, 1, up, rotation);
    Cesium.Matrix3.setColumn(rotation, 2, direction, rotation);
    //计算视锥姿态
    var orientation = Cesium.Quaternion.fromRotationMatrix(rotation, scratchOrientation);
    this.orientation = orientation;
    return orientation;
}

//创建video元素
video3D.prototype._createVideoEle = function(src) {
    //创建可视域video DOM  元素
    if (!src) return;
    var source_map4 = document.createElement("SOURCE");
    source_map4.type = 'video/mp4';
    source_map4.src = src;
    var source_mov = document.createElement("SOURCE");
    source_mov.type = 'video/quicktime';
    source_mov.src = src;
    var videoEle = document.createElement("video");

    videoEle.setAttribute('autoplay', true);
    videoEle.setAttribute('loop', true);
    videoEle.setAttribute('crossorigin', true);
    videoEle.appendChild(source_map4);
    videoEle.appendChild(source_mov);
    videoEle.style.display = 'none';
    document.body.appendChild(videoEle);
    return videoEle;
}

//获取canvas宽高
video3D.prototype._getWinWidHei = function() {
    var scene = this.viewer.scene;
    return scene.canvas.clientWidth / scene.canvas.clientHeight;
}

//创建ShadowMap
video3D.prototype._createShadowMap = function(reset) {
    let Cesium = this.Cesium;
    var camera_pos = this.cameraPosition;
    var lookat_pos = this.position;
    var scene = this.viewer.scene;
    var camera1 = new Cesium.Camera(scene);
    camera1.position = camera_pos;
    if (this._dirObj && !reset) {
        camera1.direction = this._dirObj.direction;
        camera1.right = this._dirObj.right;
        camera1.up = this._dirObj.up;
    } else {
        camera1.direction = Cesium.Cartesian3.subtract(lookat_pos, camera_pos, new Cesium.Cartesian3(0, 0, 0));
        camera1.up = Cesium.Cartesian3.normalize(camera_pos, new Cesium.Cartesian3(0, 0, 0));
    }

    var far = Cesium.Cartesian3.distance(lookat_pos, camera_pos);
    this.viewDis = far;
    camera1.frustum = new Cesium.PerspectiveFrustum({
        fov: this.fov,
        aspectRatio: this.aspectRatio,
        near: 0.1,
        far: far * 2
    });

    var isSpotLight = true;
    this.viewShadowMap = new Cesium.ShadowMap({
        lightCamera: camera1,
        enable: false,
        isPointLight: !isSpotLight,
        isSpotLight: isSpotLight,
        cascadesEnabled: false,
        context: scene.context,
        pointLightRadius: far
    });
}

//添加视椎体
video3D.prototype._addCameraFrustum = function() {
    let Cesium = this.Cesium;
    var that = this;
    this.cameraFrustum = new Cesium.Primitive({
        geometryInstances: new Cesium.GeometryInstance({
            geometry: new Cesium.FrustumOutlineGeometry({
                origin: that.cameraPosition,
                orientation: that.orientation,
                frustum: this.viewShadowMap._lightCamera.frustum,
                _drawNearPlane: true
            }),
            attributes: {
                color: Cesium.ColorGeometryInstanceAttribute.fromColor(new Cesium.Color(0.0, 0.5, 0.5))
            }
        }),
        appearance: new Cesium.PerInstanceColorAppearance({
            translucent: false,
            flat: true
        }),
        asynchronous: false,
        show: this.debugFrustum && this.show
    });
    this.viewer.scene.primitives.add(this.cameraFrustum);
}

//添加后处理
video3D.prototype._addPostProcess = function() {
    let Cesium = this.Cesium;
    var that = this;
    var bias = that.viewShadowMap._isPointLight ? that.viewShadowMap._pointBias : that.viewShadowMap._primitiveBias;
    if (!this.show) return;
    this.postProcess = new Cesium.PostProcessStage({
        fragmentShader: videoShader,
        uniforms: {
            mixNum: function mixNum() {
                return that.alpha;
            },
            marsShadow: function marsShadow() {
                return that.viewShadowMap._shadowMapTexture;
            },
            videoTexture: function videoTexture() {
                return that.videoTexture;
            },
            _shadowMap_matrix: function _shadowMap_matrix() {
                return that.viewShadowMap._shadowMapMatrix;
            },
            shadowMap_lightPositionEC: function shadowMap_lightPositionEC() {
                return that.viewShadowMap._lightPositionEC;
            },
            shadowMap_texelSizeDepthBiasAndNormalShadingSmooth: function shadowMap_texelSizeDepthBiasAndNormalShadingSmooth() {
                var texelStepSize = new Cesium.Cartesian2();
                texelStepSize.x = 1.0 / that.viewShadowMap._textureSize.x;
                texelStepSize.y = 1.0 / that.viewShadowMap._textureSize.y;
                return Cesium.Cartesian4.fromElements(texelStepSize.x, texelStepSize.y, bias.depthBias, bias.normalShadingSmooth, this.combinedUniforms1);
            },
            shadowMap_normalOffsetScaleDistanceMaxDistanceAndDarkness: function shadowMap_normalOffsetScaleDistanceMaxDistanceAndDarkness() {
                return Cesium.Cartesian4.fromElements(bias.normalOffsetScale, that.viewShadowMap._distance, that.viewShadowMap.maximumDistance, that.viewShadowMap._darkness, this.combinedUniforms2);
            },
            disViewColor: function disViewColor() {
                return that._disViewColor;
            },
            clearBlack: function clearBlack() {
                return that.clearBlack;
            }
        }
    });
    this.viewer.scene.postProcessStages.add(this.postProcess);
}

video3D.prototype.update = function(frameState) {
    this.viewShadowMap && frameState.shadowMaps.push(this.viewShadowMap);
}

video3D.prototype.destroy = function() {
    this.viewer.scene.postProcessStages.remove(this.postProcess);
    this.viewer.scene.primitives.remove(this.cameraFrustum);

    this.videoElement && this.videoElement.parentNode.removeChild(this.videoElement);
    delete this.videoElement;

    this.viewer.clock.onTick.removeEventListener(this.activeVideoListener, this);

    delete this.activeVideoListener;
    delete this.postProcess;
    delete this.viewShadowMap;
    delete this.color;
    delete this.viewDis;
    //delete this.cameraPosition;
    //delete this.position;
    //delete this.alpha;
    delete this._camerafov;
    delete this._cameraPosition;
    delete this.videoTexture;
    delete this.cameraFrustum;
    delete this.dom;
    delete this._debugFrustum;
    delete this._position;
    delete this._aspectRatio;
    delete this.orientation;
    delete this.texture;
    delete this.type;
    delete this.videoTexture;
    delete this.url;
    this.viewer.scene.primitives.remove(this);
    delete this.viewer;
}

//扩展属性
function extensionProperty(obj) {
    let video3D = obj;
    Object.defineProperty(video3D, 'alpha', {
        get: function() {
            return this._alpha;
        },
        set: function(val) {
            this._alpha = val;
        }
    })

    //相机宽高比例
    Object.defineProperty(video3D, 'aspectRatio', {
        get: function() {
            return this._aspectRatio;
        },
        set: function(val) {
            this._aspectRatio = val;
            this._changeVideoWidHei();
        }
    })

    //视椎体显隐
    Object.defineProperty(video3D, 'debugFrustum', {
        get: function() {
            return this._debugFrustum;
        },
        set: function(val) {
            this._debugFrustum = val;
            this.cameraFrustum.show = val;
        }
    })

    //相机水平张角
    Object.defineProperty(video3D, 'fov', {
        get: function() {
            return this._camerafov;
        },
        set: function(val) {
            let Cesium = this.Cesium;
            this._camerafov = Cesium.Math.toRadians(val);
            this._changeCameraFov();
        }
    })

    //相机位置
    Object.defineProperty(video3D, 'cameraPosition', {
        get: function() {
            return this._cameraPosition;
        },
        set: function(pos) {
            if (!pos) return;
            this._cameraPosition = pos;
            this._changeCameraPos();
        }
    })

    //视点位置
    Object.defineProperty(video3D, 'position', {
        get: function() {
            return this._position;
        },
        set: function(pos) {
            if (!pos) return;
            this._position = pos;
            this._changeViewPos();
        }
    })

    //切换视频 播放/暂停
    Object.defineProperty(video3D, 'videoPlay', {
        get: function() {
            return this._videoPlay;
        },
        set: function(val) {
            this._videoPlay = Boolean(val);
            if (this.videoElement) {
                if (this.videoPlay) this.videoElement.play();
                else this.videoElement.pause();
            }
        }
    })

    //所有相机的参数
    Object.defineProperty(video3D, 'params', {
        get: function() {
            let Cesium = this.Cesium;
            var viewJson = {};
            viewJson.type = this.type;
            if (this.type == "Color") viewJson.color = this.color;
            else viewJson.url = this.url;
            viewJson.position = this.position;
            viewJson.cameraPosition = this.cameraPosition;
            viewJson.fov = Cesium.Math.toDegrees(this.fov);
            viewJson.aspectRatio = this.aspectRatio;
            viewJson.alpha = this.alpha;
            viewJson.debugFrustum = this.debugFrustum;
            viewJson.dirObj = this._dirObj;
            return viewJson;
        },
    })

    //显示和隐藏
    Object.defineProperty(video3D, 'show', {
        get: function get() {
            return this.defaultShow;
        },
        set: function set(val) {
            this.defaultShow = Boolean(val);
            this._switchShow();
        }
    })


    Object.defineProperty(video3D, 'camera', {
        get: function get() {
            return this.viewShadowMap._lightCamera;
        }
    })

    Object.defineProperty(video3D, 'disViewColor', {
        get: function get() {
            return this._disViewColor;
        },
        set: function set(color) {
            if (!color) return;
            this._disViewColor = color;
            if (!color.a && color.a != 0) {
                this._disViewColor.a = 1.0;
            }
        }
    })

}

export {
    video3D,
    ratateDirection
}